/*
  author Sylvain Bertrand <sylvain.bertrand@gmail.com>
  Protected by linux GNU GPLv2
  Copyright 2012-2014
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/i2c.h>

#include <uapi/alga/pixel_fmts.h>
#include <alga/timing.h>
#include <uapi/alga/amd/dce6/dce6.h>
#include <alga/amd/dce6/dce6_dev.h>

#include "dce6.h"
#include "hpd.h"
#include "sink.h"
#include "crtc.h"

#include "regs.h"

#define RR32(of) dce->ddev.rr32(dce->ddev.dev, (of))
#define WR32(v, of) dce->ddev.wr32(dce->ddev.dev, (v), (of))

/*
 * The dce irqs are not acked by the rlc (block in charge of irq management).
 * It seems the rlc block does not have xrbm access (the block in charge of
 * register management).
 * Then those irqs must be acked manually by the CPU.
 * This is called in hard interrupt context, it does not lock the dce.
 */
void dce6_irqs_ack(struct dce6 *dce)
{
	u32 disps[CRTCS_N_MAX];
	u32 grphs[CRTCS_N_MAX];
	u8 i;
	u32 tmp;

	for (i = 0; i < dce->ddev.crtcs_n; ++i)
		disps[i] = RR32(regs_disp_int_status[i]);

	for (i = 0; i < dce->ddev.crtcs_n; ++i)
		grphs[i] = RR32(regs_crtc_grph_int_status[i]);

	/* XXX: obsolete page flipping */
	for (i = 0; i < dce->ddev.crtcs_n; ++i)
		if (grphs[i] & GIS_PFLIP_INT_OCCURRED)
			WR32(GIS_PFLIP_INT_CLR, regs_crtc_grph_int_status[i]);

	for (i = 0; i < dce->ddev.crtcs_n; ++i) {
		if (disps[i] & vals_lb_d_vblank_int[i])
			WR32(VS_ACK, regs_crtc_vblank_status[i]);
		if (disps[i] & vals_lb_d_vline_int[i])
			WR32(VS_ACK, regs_crtc_vline_status[i]);
	}

	for (i = 0; i < HPDS_N; ++i)
		if (disps[i] & vals_hpd_int[i]) {
			tmp = RR32(regs_hpd_int_ctl[i]);
			tmp |= HIC_INT_ACK;
			WR32(tmp, regs_hpd_int_ctl[i]);
		}
}
EXPORT_SYMBOL_GPL(dce6_irqs_ack);

/* this will be run in the irq thread, then not the hard context */
long dce6_irqs_thd(struct dce6 *dce)
{
	long r;
	u8 i;
	u8 hpds;

	/* grab the flagged hpd pins */
	spin_lock_irq(&dce->irq.hpds_lock);
	hpds = dce->irq.hpds;
	dce->irq.hpds = 0;
	spin_unlock_irq(&dce->irq.hpds_lock);

	r = 0;
	for (i = 0; i < HPDS_N; ++i)
		if (hpds & BIT(i)) {
			r = hpd_irq(dce, i);
			if (r != 0)
				dev_err(dce->ddev.dev, "dce6:unable to service hpd%u interrupt request\n",
									i);
		}
	return r;
}
EXPORT_SYMBOL_GPL(dce6_irqs_thd);
